﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace DiscoveryLogic.DataViz.NetworkDiagram.FieldComparers
{
    /// <summary>
    /// 
    /// </summary>
    public class EmailComparer : IAttributeComparer
    {

        private readonly Dictionary<string, string> _CountryTwoLetterCodes;

        /// <summary>
        /// 
        /// </summary>
        public EmailComparer()
        {
            Dictionary<string, string> countryCodes = new Dictionary<string, string>();
            this._CountryTwoLetterCodes = new Dictionary<string, string>();
            countryCodes.Add("Andorra", "AD");
            countryCodes.Add("United Arab Emirates", "AE");
            countryCodes.Add("Afghanistan", "AF");
            countryCodes.Add("Antigua & Barbuda", "AG");
            countryCodes.Add("Anguilla", "AI");
            countryCodes.Add("Albania", "AL");
            countryCodes.Add("Armenia", "AM");
            countryCodes.Add("Netherlands Antilles", "AN");
            countryCodes.Add("Angola", "AO");
            countryCodes.Add("Antarctica", "AQ");
            countryCodes.Add("Argentina", "AR");
            countryCodes.Add("American Samoa", "AS");
            countryCodes.Add("Austria", "AT");
            countryCodes.Add("Australia", "AU");
            countryCodes.Add("Aruba", "AW");
            countryCodes.Add("Azerbaijan", "AZ");
            countryCodes.Add("Bosnia and Herzegovina", "BA");
            countryCodes.Add("Barbados", "BB");
            countryCodes.Add("Bangladesh", "BD");
            countryCodes.Add("Belgium", "BE");
            countryCodes.Add("Burkina Faso", "BF");
            countryCodes.Add("Bulgaria", "BG");
            countryCodes.Add("Bahrain", "BH");
            countryCodes.Add("Burundi", "BI");
            countryCodes.Add("Benin", "BJ");
            countryCodes.Add("Bermuda", "BM");
            countryCodes.Add("Brunei Darussalam", "BN");
            countryCodes.Add("Bolivia", "BO");
            countryCodes.Add("Brazil", "BR");
            countryCodes.Add("Bahama", "BS");
            countryCodes.Add("Bhutan", "BT");
            countryCodes.Add("Burma (no longer exists)", "BU");
            countryCodes.Add("Bouvet Island", "BV");
            countryCodes.Add("Botswana", "BW");
            countryCodes.Add("Belarus", "BY");
            countryCodes.Add("Belize", "BZ");
            countryCodes.Add("Canada", "CA");
            countryCodes.Add("Cocos (Keeling) Islands", "CC");
            countryCodes.Add("Central African Republic", "CF");
            countryCodes.Add("Congo", "CG");
            countryCodes.Add("Switzerland", "CH");
            countryCodes.Add("Côte D'ivoire (Ivory Coast)", "CI");
            countryCodes.Add("Cook Iislands", "CK");
            countryCodes.Add("Chile", "CL");
            countryCodes.Add("Cameroon", "CM");
            countryCodes.Add("China", "CN");
            countryCodes.Add("Colombia", "CO");
            countryCodes.Add("Costa Rica", "CR");
            countryCodes.Add("Czechoslovakia (no longer exists)", "CS");
            countryCodes.Add("Cuba", "CU");
            countryCodes.Add("Cape Verde", "CV");
            countryCodes.Add("Christmas Island", "CX");
            countryCodes.Add("Cyprus", "CY");
            countryCodes.Add("Czech Republic", "CZ");
            countryCodes.Add("German Democratic Republic (no longer exists)", "DD");
            countryCodes.Add("Germany", "DE");
            countryCodes.Add("Djibouti", "DJ");
            countryCodes.Add("Denmark", "DK");
            countryCodes.Add("Dominica", "DM");
            countryCodes.Add("Dominican Republic", "DO");
            countryCodes.Add("Algeria", "DZ");
            countryCodes.Add("Ecuador", "EC");
            countryCodes.Add("Estonia", "EE");
            countryCodes.Add("Egypt", "EG");
            countryCodes.Add("Western Sahara", "EH");
            countryCodes.Add("Eritrea", "ER");
            countryCodes.Add("Spain", "ES");
            countryCodes.Add("Ethiopia", "ET");
            countryCodes.Add("Finland", "FI");
            countryCodes.Add("Fiji", "FJ");
            countryCodes.Add("Falkland Islands (Malvinas)", "FK");
            countryCodes.Add("Micronesia", "FM");
            countryCodes.Add("Faroe Islands", "FO");
            countryCodes.Add("France", "FR");
            countryCodes.Add("France, Metropolitan", "FX");
            countryCodes.Add("Gabon", "GA");
            countryCodes.Add("United Kingdom (Great Britain)", "GB");
            countryCodes.Add("Great Britain", "GB");
            countryCodes.Add("United Kingdom", "GB");
            countryCodes.Add("UK", "GB");
            countryCodes.Add("U.K.", "GB");
            countryCodes.Add("Grenada", "GD");
            countryCodes.Add("Georgia", "GE");
            countryCodes.Add("French Guiana", "GF");
            countryCodes.Add("Ghana", "GH");
            countryCodes.Add("Gibraltar", "GI");
            countryCodes.Add("Greenland", "GL");
            countryCodes.Add("Gambia", "GM");
            countryCodes.Add("Guinea", "GN");
            countryCodes.Add("Guadeloupe", "GP");
            countryCodes.Add("Equatorial Guinea", "GQ");
            countryCodes.Add("Greece", "GR");
            countryCodes.Add("South Georgia and the South Sandwich Islands", "GS");
            countryCodes.Add("Guatemala", "GT");
            countryCodes.Add("Guam", "GU");
            countryCodes.Add("Guinea-Bissau", "GW");
            countryCodes.Add("Guyana", "GY");
            countryCodes.Add("Hong Kong", "HK");
            countryCodes.Add("Heard & McDonald Islands", "HM");
            countryCodes.Add("Honduras", "HN");
            countryCodes.Add("Croatia", "HR");
            countryCodes.Add("Haiti", "HT");
            countryCodes.Add("Hungary", "HU");
            countryCodes.Add("Indonesia", "ID");
            countryCodes.Add("Ireland", "IE");
            countryCodes.Add("Israel", "IL");
            countryCodes.Add("India", "IN");
            countryCodes.Add("British Indian Ocean Territory", "IO");
            countryCodes.Add("Iraq", "IQ");
            countryCodes.Add("Islamic Republic of Iran", "IR");
            countryCodes.Add("Iceland", "IS");
            countryCodes.Add("Italy", "IT");
            countryCodes.Add("Jamaica", "JM");
            countryCodes.Add("Jordan", "JO");
            countryCodes.Add("Japan", "JP");
            countryCodes.Add("Kenya", "KE");
            countryCodes.Add("Kyrgyzstan", "KG");
            countryCodes.Add("Cambodia", "KH");
            countryCodes.Add("Kiribati", "KI");
            countryCodes.Add("Comoros", "KM");
            countryCodes.Add("St. Kitts and Nevis", "KN");
            countryCodes.Add("Korea, Democratic People's Republic of", "KP");
            countryCodes.Add("Korea, Republic of", "KR");
            countryCodes.Add("Kuwait", "KW");
            countryCodes.Add("Cayman Islands", "KY");
            countryCodes.Add("Kazakhstan", "KZ");
            countryCodes.Add("Lao People's Democratic Republic", "LA");
            countryCodes.Add("Lebanon", "LB");
            countryCodes.Add("Saint Lucia", "LC");
            countryCodes.Add("Liechtenstein", "LI");
            countryCodes.Add("Sri Lanka", "LK");
            countryCodes.Add("Liberia", "LR");
            countryCodes.Add("Lesotho", "LS");
            countryCodes.Add("Lithuania", "LT");
            countryCodes.Add("Luxembourg", "LU");
            countryCodes.Add("Latvia", "LV");
            countryCodes.Add("Libyan Arab Jamahiriya", "LY");
            countryCodes.Add("Morocco", "MA");
            countryCodes.Add("Monaco", "MC");
            countryCodes.Add("Moldova, Republic of", "MD");
            countryCodes.Add("Madagascar", "MG");
            countryCodes.Add("Marshall Islands", "MH");
            countryCodes.Add("Mali", "ML");
            countryCodes.Add("Mongolia", "MN");
            countryCodes.Add("Myanmar", "MM");
            countryCodes.Add("Macau", "MO");
            countryCodes.Add("Northern Mariana Islands", "MP");
            countryCodes.Add("Martinique", "MQ");
            countryCodes.Add("Mauritania", "MR");
            countryCodes.Add("Monserrat", "MS");
            countryCodes.Add("Malta", "MT");
            countryCodes.Add("Mauritius", "MU");
            countryCodes.Add("Maldives", "MV");
            countryCodes.Add("Malawi", "MW");
            countryCodes.Add("Mexico", "MX");
            countryCodes.Add("Malaysia", "MY");
            countryCodes.Add("Mozambique", "MZ");
            countryCodes.Add("Namibia", "NA");
            countryCodes.Add("New Caledonia", "NC");
            countryCodes.Add("Niger", "NE");
            countryCodes.Add("Norfolk Island", "NF");
            countryCodes.Add("Nigeria", "NG");
            countryCodes.Add("Nicaragua", "NI");
            countryCodes.Add("Netherlands", "NL");
            countryCodes.Add("Norway", "NO");
            countryCodes.Add("Nepal", "NP");
            countryCodes.Add("Nauru", "NR");
            countryCodes.Add("Neutral Zone (no longer exists)", "NT");
            countryCodes.Add("Niue", "NU");
            countryCodes.Add("New Zealand", "NZ");
            countryCodes.Add("Oman", "OM");
            countryCodes.Add("Panama", "PA");
            countryCodes.Add("Peru", "PE");
            countryCodes.Add("French Polynesia", "PF");
            countryCodes.Add("Papua New Guinea", "PG");
            countryCodes.Add("Philippines", "PH");
            countryCodes.Add("Pakistan", "PK");
            countryCodes.Add("Poland", "PL");
            countryCodes.Add("St. Pierre & Miquelon", "PM");
            countryCodes.Add("Pitcairn", "PN");
            countryCodes.Add("Puerto Rico", "PR");
            countryCodes.Add("Portugal", "PT");
            countryCodes.Add("Palau", "PW");
            countryCodes.Add("Paraguay", "PY");
            countryCodes.Add("Qatar", "QA");
            countryCodes.Add("Réunion", "RE");
            countryCodes.Add("Romania", "RO");
            countryCodes.Add("Russian Federation", "RU");
            countryCodes.Add("Rwanda", "RW");
            countryCodes.Add("Saudi Arabia", "SA");
            countryCodes.Add("Solomon Islands", "SB");
            countryCodes.Add("Seychelles", "SC");
            countryCodes.Add("Sudan", "SD");
            countryCodes.Add("Sweden", "SE");
            countryCodes.Add("Singapore", "SG");
            countryCodes.Add("St. Helena", "SH");
            countryCodes.Add("Slovenia", "SI");
            countryCodes.Add("Svalbard & Jan Mayen Islands", "SJ");
            countryCodes.Add("Slovakia", "SK");
            countryCodes.Add("Sierra Leone", "SL");
            countryCodes.Add("San Marino", "SM");
            countryCodes.Add("Senegal", "SN");
            countryCodes.Add("Somalia", "SO");
            countryCodes.Add("Suriname", "SR");
            countryCodes.Add("Sao Tome & Principe", "ST");
            countryCodes.Add("Union of Soviet Socialist Republics (no longer exists)", "SU");
            countryCodes.Add("El Salvador", "SV");
            countryCodes.Add("Syrian Arab Republic", "SY");
            countryCodes.Add("Swaziland", "SZ");
            countryCodes.Add("Turks & Caicos Islands", "TC");
            countryCodes.Add("Chad", "TD");
            countryCodes.Add("French Southern Territories", "TF");
            countryCodes.Add("Togo", "TG");
            countryCodes.Add("Thailand", "TH");
            countryCodes.Add("Tajikistan", "TJ");
            countryCodes.Add("Tokelau", "TK");
            countryCodes.Add("Turkmenistan", "TM");
            countryCodes.Add("Tunisia", "TN");
            countryCodes.Add("Tonga", "TO");
            countryCodes.Add("East Timor", "TP");
            countryCodes.Add("Turkey", "TR");
            countryCodes.Add("Trinidad & Tobago", "TT");
            countryCodes.Add("Tuvalu", "TV");
            countryCodes.Add("Taiwan, Province of China", "TW");
            countryCodes.Add("Tanzania, United Republic of", "TZ");
            countryCodes.Add("Ukraine", "UA");
            countryCodes.Add("Uganda", "UG");
            countryCodes.Add("United States Minor Outlying Islands", "UM");
            countryCodes.Add("United States of America", "US");
            countryCodes.Add("USA", "US");
            countryCodes.Add("U.S.A.", "US");
            countryCodes.Add("United States", "US");
            countryCodes.Add("Uruguay", "UY");
            countryCodes.Add("Uzbekistan", "UZ");
            countryCodes.Add("Vatican City State (Holy See)", "VA");
            countryCodes.Add("St. Vincent & the Grenadines", "VC");
            countryCodes.Add("Venezuela", "VE");
            countryCodes.Add("British Virgin Islands", "VG");
            countryCodes.Add("United States Virgin Islands", "VI");
            countryCodes.Add("Viet Nam", "VN");
            countryCodes.Add("Vanuatu", "VU");
            countryCodes.Add("Wallis & Futuna Islands", "WF");
            countryCodes.Add("Samoa", "WS");
            countryCodes.Add("Democratic Yemen (no longer exists)", "YD");
            countryCodes.Add("Yemen", "YE");
            countryCodes.Add("Mayotte", "YT");
            countryCodes.Add("Yugoslavia", "YU");
            countryCodes.Add("South Africa", "ZA");
            countryCodes.Add("Zambia", "ZM");
            countryCodes.Add("Zaire", "ZR");
            countryCodes.Add("Zimbabwe", "ZW");

            foreach (string countryName in countryCodes.Keys)
            {
                string countryCode = countryCodes[countryName];
                if (!this._CountryTwoLetterCodes.ContainsKey(countryCode))
                    this._CountryTwoLetterCodes.Add(countryCode, countryName);
            }
        }

        #region Implementation of IAttributeComparer

        /// <summary>
        /// specify either use code snippet or class
        /// </summary>
        public AttributeComparerType ComparerType
        {
            get
            {
                return AttributeComparerType.ClassComparer;
            }
        }

        private SqlDbType _FieldDbType;
        ///<summary>
        ///</summary>
        public SqlDbType FieldDbType
        {
            get { return _FieldDbType; }
            set { _FieldDbType = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public IAttributeComparer Create()
        {
            return new EmailComparer();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value1"></param>
        /// <param name="value2"></param>
        /// <param name="penalty"></param>
        /// <param name="conn"></param>
        /// <param name="unevenDistTableName"></param>
        /// <returns></returns>
        public double Compare(object value1, object value2, double penalty, SqlConnection conn, string unevenDistTableName)
        {
            if (value1 == null || value1 == null)
                return 0;

            string strVal1 = value1.ToString();
            string strVal2 = value2.ToString();
            if (strVal1.Length == 0 || strVal2.Length == 0)
                return 0;

            if (strVal1.ToLower() == strVal2.ToLower())
            {
                return 1.0;
            }

            string country1 = "US";
            string country2 = "US";
            if (strVal1.LastIndexOf(".") > 0)
            {
                string cName1 = strVal1.Substring(strVal1.LastIndexOf("."));
                if (cName1.Length == 2)
                {
                    cName1 = cName1.ToUpper();
                    if (this._CountryTwoLetterCodes.ContainsKey(cName1))
                    {
                        country1 = this._CountryTwoLetterCodes[cName1];
                        strVal1 = strVal1.Substring(0, strVal1.LastIndexOf("."));
                    }
                }
            }
            if (strVal2.LastIndexOf(".") > 0)
            {
                string cName2 = strVal2.Substring(strVal2.LastIndexOf("."));
                if (cName2.Length == 2)
                {
                    cName2 = cName2.ToUpper();
                    if (this._CountryTwoLetterCodes.ContainsKey(cName2))
                    {
                        country2 = this._CountryTwoLetterCodes[cName2];
                        strVal2 = strVal2.Substring(0, strVal2.LastIndexOf("."));
                    }
                }
            }

            string emailRegexPattern = "^([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\\.[A-Z]{2,4}$";
            if (Regex.IsMatch(strVal1, emailRegexPattern) && Regex.IsMatch(strVal2, emailRegexPattern))
            {
                string userName1 = Regex.Match(strVal1, emailRegexPattern, RegexOptions.IgnoreCase).Groups[1].Value;
                string userName2 = Regex.Match(strVal2, emailRegexPattern, RegexOptions.IgnoreCase).Groups[1].Value;
                string domain1 = Regex.Match(strVal1, emailRegexPattern, RegexOptions.IgnoreCase).Groups[2].Value;
                if (domain1.IndexOf(".") > 0)
                {
                    domain1 = domain1.Substring(domain1.LastIndexOf(".") + 1);
                }
                string domain2 = Regex.Match(strVal2, emailRegexPattern, RegexOptions.IgnoreCase).Groups[2].Value;
                if (domain2.IndexOf(".") > 0)
                {
                    domain2 = domain2.Substring(domain2.LastIndexOf(".") + 1);
                }
                if (userName1.ToLower() == userName2.ToLower() && domain1.ToLower() == domain2.ToLower())
                {
                    if (country1 == country2)
                        return 0.95;
                    else
                        return 0.80;
                }
            }

            return 0;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="values"></param>
        /// <param name="conn"></param>
        /// <param name="unevenDistTableName"></param>
        /// <returns></returns>
        public object Merge(List<object> values, SqlConnection conn, string unevenDistTableName)
        {
            if (values == null || values.Count == 0)
                return null;

            Dictionary<object, int> countByValues = new Dictionary<object, int>();
            foreach (object obj in values)
            {
                if (obj == null)
                    continue;

                if (!countByValues.ContainsKey(obj))
                    countByValues.Add(obj, 1);
                else
                    countByValues[obj] = countByValues[obj] + 1;
            }

            // check ssn9 first
            if (countByValues.Count > 0)
            {
                object mode = null;
                int count = 0;
                foreach (object obj in countByValues.Keys)
                {
                    if (countByValues[obj] > count)
                    {
                        count = countByValues[obj];
                        mode = obj;
                    }
                }
                return mode;
            }
            else
                return null;
        }

        #endregion
    }
}
